/* ----------------------------------------------------------------------------
* File: api_event_dispatcher_impl.cpp
*
* Desc: Dispatcher for events implementation
*
* Created: 06/09/2011, by Alexey Korshunov
* ---------------------------------------------------------------------------- */
#include "api_event.h"
#include "api_event_dispatcher_impl.h"

#define IID_EVENT_SINK						2
#define WAIT_TIME_EVENT_DISPATCHER			0		// ms
#define WAIT_TIME_ATTACH_EVENT_DISPATCHER	200		// ms

namespace SkypeProtection
{
	namespace
	{
		class EventSink
			: public IDispEventImpl<IID_EVENT_SINK, EventSink, &__uuidof(SKYPE4COMLib::_ISkypeEvents), &SKYPE4COMLib::LIBID_SKYPE4COMLib, 1, 0>
		{
		public:
			explicit EventSink(APIEventHandler* handler)
				: m_EventHandler(handler)
			{
			}

			// Event dispatching
			void __stdcall Command(SKYPE4COMLib::ICommand* pCommand)
			{
				if (m_EventHandler != NULL)
					m_EventHandler->OnCommand(pCommand);
			}

			void __stdcall Reply(SKYPE4COMLib::ICommand* pCommand)
			{
				if (m_EventHandler != NULL)
					m_EventHandler->OnReply(pCommand);
			}

			void __stdcall Error(SKYPE4COMLib::ICommand* pCommand, LONG Number, BSTR Description)
			{
				//_tprintf(_T("E(%d): %s\n"), pCommand->GetId(), (LPCTSTR)pCommand->GetReply());
				if (m_EventHandler != NULL)
					m_EventHandler->OnError(pCommand, Number, Description);
			}

			void __stdcall AttachmentStatus(SKYPE4COMLib::TAttachmentStatus Status)
			{
				//_tprintf(_T("Attachment status %d\n"), Status);
				if (m_EventHandler != NULL)
					m_EventHandler->OnAttachmentStatus(Status);
			}

			void __stdcall ConnectionStatus(SKYPE4COMLib::TConnectionStatus Status)
			{
				//_tprintf(_T("Connection status %d\n"), Status);
				if (m_EventHandler != NULL)
					m_EventHandler->OnConnectionStatus(Status);
			}

			void __stdcall UserStatus(SKYPE4COMLib::TUserStatus Status)
			{
				//_tprintf(_T("User status %d\n"), Status);
				if (m_EventHandler != NULL)
					m_EventHandler->OnUserStatus(Status);
			}

			void __stdcall OnlineStatus(SKYPE4COMLib::IUser* pUser, SKYPE4COMLib::TOnlineStatus Status)
			{
				//_tprintf(_T("User %s status %d\n"), (LPCTSTR)pUser->GetHandle(), Status);
				if (m_EventHandler != NULL)
					m_EventHandler->OnOnlineStatus(pUser, Status);
			}

			void __stdcall CallStatus(SKYPE4COMLib::ICall* pCall, SKYPE4COMLib::TCallStatus Status)
			{
				//_tprintf(_T("Call %d status %d\n"), pCall->GetId(), Status);
				switch(Status)
				{
				case SKYPE4COMLib::TCallStatus::clsInProgress:
				case SKYPE4COMLib::TCallStatus::clsRouting:
				case SKYPE4COMLib::TCallStatus::clsRinging:
				case SKYPE4COMLib::TCallStatus::clsFailed:
				case SKYPE4COMLib::TCallStatus::clsFinished:
					break;
				}

				if (m_EventHandler != NULL)
					m_EventHandler->OnCallStatus(pCall, Status);
			}

			void __stdcall CallHistory()
			{
				//_tprintf(_T("Call history changed\n"));
				if (m_EventHandler != NULL)
					m_EventHandler->OnCallHistory();
			}

			void __stdcall Mute(VARIANT_BOOL Status)
			{      
				if (m_EventHandler != NULL)
					m_EventHandler->OnMute(Status);
			}

			void __stdcall MessageStatus(SKYPE4COMLib::IChatMessage* pMessage, SKYPE4COMLib::TChatMessageStatus Status)
			{
				if (m_EventHandler != NULL)
					m_EventHandler->OnMessageStatus(pMessage, Status);
			}

			void __stdcall MessageHistory(BSTR Username)
			{
				//_tprintf(_T("Message history changed %s\n"), (LPCTSTR)_bstr_t(Username));
				if (m_EventHandler != NULL)
					m_EventHandler->OnMessageHistory(Username);
			}

			BEGIN_SINK_MAP(EventSink)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 1, &Command,  &CommandInfo)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 2, &Reply,  &ReplyInfo)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 3, &Error,  &ErrorInfo)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 4, &AttachmentStatus, &AttachmentStatusInfo)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 5, &ConnectionStatus,  &ConnectionStatusInfo)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 6, &UserStatus,  &UserStatusInfo)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 7, &OnlineStatus,  &OnlineStatusInfo)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 8, &CallStatus,  &CallStatusInfo)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 10, &Mute,  &MuteInfo)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 11, &MessageStatus,  &MessageStatusInfo)
				SINK_ENTRY_INFO(IID_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 12, &MessageHistory,  &MessageHistoryInfo)
			END_SINK_MAP()

		private:
			APIEventHandler* m_EventHandler;
		};
	}

	APIEventDispatcherImpl::APIEventDispatcherImpl(const APIEventDispatcherParams& params)
		: APIProxy(false)
	{
		m_EventHandler.reset(params.EventHandler);

		if (Start() != Common::SKYPE_SUCCESS)
			throw Common::Exception("APIEventDispatcher: ERROR - Unable to start dispatcher");
	}

	APIEventDispatcherImpl::~APIEventDispatcherImpl()
	{
		Stop();
	}

	Common::Error APIEventDispatcherImpl::RunLogic(SKYPE4COMLib::ISkype* skype)
	{
		std::auto_ptr<EventSink> eventSink(new EventSink(m_EventHandler.get()));

		if (StartProxyProcess(skype, eventSink.get()) != Common::SKYPE_SUCCESS)
			return Common::SKYPE_ERROR;

		MSG msg;
		while(!QueryExitStatus(WAIT_TIME_EVENT_DISPATCHER) && GetMessage(&msg, NULL, 0, 0 ))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		StopProxyProcess(skype, eventSink.get());
		return Common::SKYPE_SUCCESS;
	}

	Common::Error APIEventDispatcherImpl::StartProxyProcess(SKYPE4COMLib::ISkype* skype, EventSink* sink)
	{
		m_Logger->Print("APIEventDispatcher::StartProxyProcess() - start");

		// Advise event listener
		HRESULT hr = sink->DispEventAdvise(skype);
		if (hr != S_OK)
		{
			m_Logger->Print("APIEventDispatcher: ERROR - Unable to advise event listener");
			throw Common::Exception("APIEventDispatcher: ERROR - Unable to advise event listener");
		}
		m_Logger->Print("APIEventDispatcher: advised event listener (OK)");

		// Start client
		if (skype->GetClient()->IsRunning == VARIANT_FALSE)
			skype->GetClient()->Start(VARIANT_TRUE, VARIANT_FALSE);

		// Connect to Skype API without waiting
		skype->Attach(6, VARIANT_FALSE);

		// Wait API permission
		SKYPE4COMLib::TAttachmentStatus attachStatus = skype->GetAttachmentStatus();
		while (attachStatus != SKYPE4COMLib::TAttachmentStatus::apiAttachSuccess)
		{
			if (attachStatus == SKYPE4COMLib::TAttachmentStatus::apiAttachRefused)
			{
				sink->DispEventUnadvise(skype);
				m_Logger->Print("APIEventDispatcher: attach refused (FAILED)");
				throw Common::Exception("Object was refused");
			}
			else
			{
				// notify about attaching process
				//if (m_AttachHandler.get())
				//	m_AttachHandler->OnAttaching();
			}

			Sleep(WAIT_TIME_ATTACH_EVENT_DISPATCHER);
			attachStatus = skype->GetAttachmentStatus();
		}

		m_Logger->Print("APIEventDispatcher: attached (OK)");
		return Common::SKYPE_SUCCESS;
	}

	Common::Error APIEventDispatcherImpl::StopProxyProcess(SKYPE4COMLib::ISkype* skype, EventSink* sink)
	{
		sink->DispEventUnadvise(skype);
		return Common::SKYPE_SUCCESS;
	}

	std::auto_ptr<APIEventDispatcher> CreateAPIEventDispatcher(const APIEventDispatcherParams& params)
	{
		std::auto_ptr<APIEventDispatcher> dispatcher;
		try
		{
			std::auto_ptr<APIEventDispatcherImpl> obj(new APIEventDispatcherImpl(params));
			dispatcher = obj;
		}
		catch (...)
		{
		}
		return dispatcher;
	}
}